package com.luo.d3s.core.util.validation.constraints;

import com.luo.d3s.core.exception.ValidateException;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Enum Range validator
 *
 * @author luohq
 * @date 2023-06-16
 */
public class EnumRangeValidator implements ConstraintValidator<EnumRange, Object> {

    /**
     * 默认name方法名
     */
    private final String DEFAULT_ENUM_NAME_METHOD = "name";
    /**
     * ordinal方法名
     */
    private final String ENUM_ORDINAL_METHOD = "ordinal";

    /**
     * 枚举验证注解
     */
    private EnumRange enumRangeAnno;

    /**
     * 枚举值范围
     */
    private Set<Object> enumValueSet;

    @Override
    public void initialize(EnumRange enumRange) {
        this.enumRangeAnno = enumRange;
        //初始提取全部枚举值
        this.enumValueSet = this.extractEnumValueSet();
    }

    @Override
    public boolean isValid(Object enumValue, ConstraintValidatorContext cxt) {
        //待验证值为空则直接返回
        if (null == enumValue) {
            return true;
        }
        //验证值是否在枚举范围内
        return this.enumValueSet.contains(enumValue);
    }

    /**
     * 提取枚举值集合
     *
     * @return 枚举值集合
     */
    private Set<Object> extractEnumValueSet() {
        //提取枚举数组
        Enum[] enumConstantsArray = this.enumRangeAnno.value().getEnumConstants();
        //转换具体待比较的枚举值集合
        return Stream.of(enumConstantsArray).map(this::extractEnumValue).collect(Collectors.toSet());
    }

    /**
     * 提取单个枚举值
     *
     * @param curEnum 当前枚举
     * @return 枚举值
     */
    private Object extractEnumValue(Enum curEnum) {
        //提取注解定义的枚举值方法名
        String enumValueMethodName = this.enumRangeAnno.enumValueMethod();

        //特殊处理name和ordinal方法
        if (this.DEFAULT_ENUM_NAME_METHOD.equals(enumValueMethodName)) {
            return curEnum.name();
        }
        if (this.ENUM_ORDINAL_METHOD.equals(enumValueMethodName)) {
            return String.valueOf(curEnum.ordinal());
        }

        //反射获取枚举值
        try {
            Method enumValueMethod = curEnum.getClass().getMethod(enumValueMethodName);
            return enumValueMethod.invoke(curEnum);
        } catch (Exception ex) {
            String validateMsg = String.format("枚举范围验证异常，请检查枚举验证配置@EnumRange.enumValueMethod是否正确: %s", curEnum.getClass().getName());
            throw new ValidateException(validateMsg, ex);
        }
    }

}